// CSE571 - game.cpp
// Authors: Jonathan Wald, Daniel Sarfati, Guangle Fan
// Game class responsible for managing a game of poker.

#include <iostream>
#include <sstream>
#include "stdio.h"
#include <map>
#include <iterator>
#include <utility>
#include <algorithm>
#include "globals.h"
#include "game.h"
#include "player.h"
#include "io-manager.h"

const unsigned int PLAYERS_REQUIRED = 5;
const unsigned int NUM_CARDS = 5;
const unsigned int NUM_HELLO_CARDS = 2;
const unsigned int NUM_TABLE_CARDS = 5;
const unsigned int MAX_PLAYERS = 10;


typedef Player* PlayerPtr;

ostream& operator<< (ostream& os, const PlayerPtr p) {
  os << p->name << ": " << p->score <<
	" (" << p->hand << ")" << std::endl;
  return os;
}

struct print_player_name
  : std::unary_function<Player*, void> {
  void operator() (Player* p) {
    cout << "\t\t\t" << p->name << std::endl;
  }
};

struct send_hand_to_player
  : std::unary_function<Player*, void> {
  send_hand_to_player(Game* game) {
    game_ = game;
  }

  void operator() (Player* player) {
    game_->send_hand(player);
  }

  Game* game_;
};

Game::Game() : suspended(false) {
  init();
}

void Game::init() {
  deck.emptyDeck();
  scores_received_ = 0;
  winner_announced_ = false;
  Card c;
  //Fill up our deck with one of each card in a standard deck   
  for(unsigned int suit = Card::club; suit <= Card::spade; ++suit){
    c.c_suit = (Card::suit)suit;
      
    for(unsigned int rank = Card::two; rank <= Card::ace; ++rank){
      c.c_rank = (Card::rank)rank;
      deck.add_card(c);
    }
  }
  
  // Shuffle deck, seeding is now done when server is initialized.
  deck.shuffle();
  running = false;
}

void Game::add_card(Card& card) {
  deck.add_card(card);
  deck.shuffle();
}

void Game::score_added() {
  ++scores_received_;  
  if ((unsigned int) scores_received_ == players.size()) {
    running = false;
    sort(players.begin(), players.end(), player_ptr_less());
    std::cout << "Scores for " << name << ":" << std::endl;
    copy(players.begin(), players.end(),
	 ostream_iterator<PlayerPtr>(cout));
    winner_announced_ = true;

    // Don't delete players or handlers, we will reuse them. But notify players
    // the game is over and they can start a new one by setting their game to
    // NULL and delete this game instance.
    Dealer::instance()->games.erase (name);
    for(unsigned int i = 0; i < players.size(); ++i) {
      players[i]->reset();
    }

    // We create a new game object for the next round, but we keep this one
    // for suspension/resume purposes, will be cleaned up once the new round
    // is initiated.
    Dealer::instance()->finished_games.insert(std::pair<string, Game*>(name,
								       this));

  }
}

void Game::send_hand(Player* player) {
  if(player->game->suspended) {
    player->mask = player->mask | ACE_Event_Handler::WRITE_MASK;
  }
   if (player->game->type == Game::HOLDEM) {
    // if game type is holdem, server will send five talbe cards first and then two hello cards.
    for(unsigned int i = 0; i < tableCards.size(); ++i)
      player->send_card(tableCards[i]);
    for(unsigned int i = 0; i < NUM_HELLO_CARDS; ++i) {
      int last_card = deck.size() - 1;
      player->send_card(deck[last_card]);
      deck.eraseCard(last_card);
    }
  } else {
    for(unsigned int i = 0; i < NUM_CARDS; ++i) {
      int last_card = deck.size() - 1;
      player->send_card(deck[last_card]);
      player->hand.add_card(deck[last_card]);
      deck.eraseCard(last_card);
    }
  }
}

void Game::send_card(Player* player) {
  int last_card = deck.size() - 1;
  player->send_card(deck[last_card]);
  player->hand.add_card(deck[last_card]);
  deck.eraseCard(last_card);
}

void Game::new_player_added() {
  if (players.size() > MAX_PLAYERS) {
    players.pop_back();
    return;
  }

  // If we have just added a player and now have 5, then the game is about to
  // begin so we should send cards to all players.
  if (players.size() == PLAYERS_REQUIRED) {
    running = true;
    for_each(players.begin(), players.end(), send_hand_to_player(this));
  }  else if (players.size() > (unsigned int) PLAYERS_REQUIRED) {
    // If we have more than 5 players, the game has already begun but we
    // might still be able to include the new player, so check if the game
    // is still running.
    if (running) {
      send_hand(players.back());
    }
  }
}

void Game::player_quit(Player* quitPlayer) {  //one player quit
  if (quitPlayer->score != -1) {
    // Already received score.  This will be the case when user
    // calls "define" from interface.  Don't delete, but tell game that this
    // player is done so it can delete after score have bene printed.
    quitPlayer->quitting = true;
    return;
  }

  // Delete player from game
  vector<Player *>::iterator ptemp, piter;
  for (piter = players.begin(); piter != players.end(); ++piter) {
    if (*piter == quitPlayer) ptemp = piter;
  }
  players.erase (ptemp);

  // Delete player from dealer's records
  map<string, Player *>::iterator player_iter;
  Dealer* dealer = Dealer::instance();
  for(player_iter = dealer->players.begin(); player_iter != dealer->players.end();
      ++player_iter) {
    if (player_iter->second == quitPlayer) {
      dealer->players.erase(player_iter);
      break; 
    }
  }


  if (running && quitPlayer->score == -1) {    //sent hand but haven't receive his score
    if (players.size()  >= (unsigned int) PLAYERS_REQUIRED) {  //still enough player
      vector<Card> vc = quitPlayer->hand.getHand();
      for (unsigned int i = 0; i < vc.size(); ++i) {
	deck.add_card(vc[i]);
      }
      deck.reseed();
      deck.shuffle();
    }
    else {   //not enough players
      for (unsigned int i = 0; i < players.size(); ++i) {
    	players[i]->send_msg(MISS_DEAL_MSG);
    	players[i]->hand.emptyHand();
      }
      tableCards.emptyDeck();
      restart();
    }
  }
    
  cout << quitPlayer->name << " quit from game: " << name << endl;

  if (players.size() == 0) {
    Dealer::instance()->games.erase (name);
    delete this;
  }
}

void Game::restart() {
  deck.emptyDeck();
  scores_received_ = 0;
  winner_announced_ = false;
  Card c;
  //Fill up our deck with one of each card in a standard deck
   
  for(unsigned int suit = Card::club; suit <= Card::spade; ++suit){
    c.c_suit = (Card::suit)suit;
      
    for(unsigned int rank = Card::two; rank <= Card::ace; ++rank){
      c.c_rank = (Card::rank)rank;
      deck.add_card(c);
    }
  }
  
  //shuffle deck
  deck.shuffle();
  running = false;
  if (this->type == HOLDEM) initTableCards();
}

void Game::printPlayersString() {
  std::cout << "\t\t" << name << ":" << std::endl;
  for_each(players.begin(), players.end(), print_player_name());
}

void Game::initTableCards() {
  for(unsigned int i = 0; i < NUM_TABLE_CARDS; ++i) {
    int last_card = deck.size() - 1;
    tableCards.add_card(deck[last_card]);
    deck.eraseCard(last_card);
  }
}

Game::~Game() {
  for(unsigned int i = 0; i < players.size(); ++i) {
    if (players[i]->quitting) {
      players[i]->send_msg(REMOVE_HANDLER);
      std::cout << players[i]->name << std::endl;
    }
  }

}

// Service methods  
int Game::init (int, char *[]) {

  return 0;
}

int Game::fini (void) {
  return 0;
}

int Game::suspend (void) {
  if (suspended) {
    std::cout << name << " is already suspended." << std::endl;
    return -1;
  }
  suspended = true;
  for(unsigned int i = 0; i < players.size(); ++i) {
    IOManager::instance()->enqueue_suspend(&(players[i]->handler));
  }
  return 0;
}

int Game::resume (void) {
  if (!suspended) {
    return -1;
  }
  suspended = false;
  for(unsigned int i = 0; i < players.size(); ++i) {
    IOManager::instance()->enqueue_resume(&(players[i]->handler));
  }
  return 0;
}

int Game::info (char **, size_t) {
  std::cout << "\tService Info - Label: " << label << " Name: " << name << " Status: "
            << (suspended ? std::string("Suspended") : std::string ("Running"))
            << std::endl;
  if(winner_announced_) {
    std::cout << "\t\tGame is waiting for new round to start." << std::endl;
  } else {
    printPlayersString();
  }
  return 0;
}
